package pl.edu.agh.neuraleconomy.ui.component;

import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DateTime;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scale;
import org.jfree.chart.JFreeChart;

import pl.edu.agh.neuraleconomy.common.utils.DateUtils;
import pl.edu.agh.neuraleconomy.core.nn.NetworkStructure;
import pl.edu.agh.neuraleconomy.core.nn.transformer.DailyChangeDataTransformer;
import pl.edu.agh.neuraleconomy.core.nn.transformer.IDataTransformer;
import pl.edu.agh.neuraleconomy.core.ta.indicator.Indicator;
import pl.edu.agh.neuraleconomy.model.exchange.Company;
import pl.edu.agh.neuraleconomy.model.exchange.Exchange;
import pl.edu.agh.neuraleconomy.persistence.base.DaoProvider;
import pl.edu.agh.neuraleconomy.persistence.exchange.CompanyDao;
import pl.edu.agh.neuraleconomy.persistence.exchange.ExchangeDao;
import pl.edu.agh.neuraleconomy.ui.chart.ChartBean;
import pl.edu.agh.neuraleconomy.ui.chart.LineChart;
import pl.edu.agh.neuraleconomy.ui.types.PredictionDataComposite;
import pl.edu.agh.neuraleconomy.ui.util.UIUtils;

public class ChartPanel extends Composite {
	private static Composite leftPanel;
	private static Composite centerPanel;
	private static Composite bottomPanel;
	private static Canvas chartCanvas;
	private static Image chartImage;
	private static JFreeChart chart;

	private static DateTime dateFrom;
	private static DateTime dateTo;
	private static Combo companyCombo;

	private static Scale timelineScale;
	private static Label valueLabel;
	private static Label dateLabel;
	private static SelectionListener scaleListener;

	private static IndicatorsPanel indicatorsPanel;
	private PredictionPanel predictionPanel;

	public ChartPanel(Composite parent, int style) {
		super(parent, style);
		init();
	}

	private void init() {
		setLayout();
		addLeftPanel();
		addCenterPanel();
		addBottomPanel();
	}

	private void setLayout() {
		GridLayout gridLayout = new GridLayout();
		gridLayout.numColumns = 2;
		this.setLayout(gridLayout);
		this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
	}

	private void addLeftPanel() {
		leftPanel = new Composite(this, SWT.BORDER);
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		leftPanel.setLayout(layout);
		GridData gridData = new GridData(GridData.BEGINNING, GridData.FILL, false, false);
		leftPanel.setLayoutData(gridData);

		companyCombo = new Combo(leftPanel, SWT.READ_ONLY);
		gridData = new GridData();
		gridData.horizontalAlignment = SWT.FILL;
		gridData.horizontalSpan = 2;
		companyCombo.setLayoutData(gridData);
		populateCompanyCombo(companyCombo);

		(new Label(leftPanel, SWT.NONE)).setText("Date from");
		dateFrom = new DateTime(leftPanel, SWT.DATE | SWT.DROP_DOWN);

		(new Label(leftPanel, SWT.NONE)).setText("Date to");
		dateTo = new DateTime(leftPanel, SWT.DATE | SWT.DROP_DOWN);

		gridData = new GridData();
		gridData.horizontalAlignment = SWT.FILL;
		gridData.horizontalSpan = 2;

		indicatorsPanel = new IndicatorsPanel(leftPanel, SWT.NONE);
		indicatorsPanel.setLayoutData(gridData);

		gridData = new GridData();
		gridData.horizontalAlignment = SWT.FILL;
		gridData.horizontalSpan = 2;

		predictionPanel = new PredictionPanel(leftPanel, SWT.NONE);
		predictionPanel.setLayoutData(gridData);

		gridData = new GridData();
		gridData.horizontalAlignment = SWT.FILL;
		gridData.verticalAlignment = SWT.BEGINNING;
		gridData.horizontalSpan = 2;

		Button repaintButton = new Button(leftPanel, SWT.NONE);
		repaintButton.setLayoutData(gridData);
		repaintButton.setText("Repaint");
		repaintButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				paint();
			}
		});
	}

	private void addCenterPanel() {
		centerPanel = new Composite(this, SWT.BORDER);
		centerPanel.setLayout(new GridLayout());
		GridData gridData = new GridData(GridData.FILL, GridData.FILL, true, true);
		gridData.widthHint = 600;
		gridData.heightHint = 300;
		centerPanel.setLayoutData(gridData);

		chartCanvas = new Canvas(centerPanel, SWT.NONE);
		gridData = new GridData(GridData.FILL, GridData.FILL, true, true);
		chartCanvas.setLayoutData(gridData);
		chartCanvas.addPaintListener(new PaintListener() {
			public void paintControl(final PaintEvent event) {
				refreshChartImage();
				if (chartImage != null) {
					event.gc.drawImage(chartImage, 0, 0);
				}
			}
		});
	}

	private void addBottomPanel() {
		bottomPanel = new Composite(this, SWT.BORDER);

		GridData data = new GridData();
		data.horizontalSpan = 2;
		data.verticalAlignment = SWT.FILL;
		data.horizontalAlignment = SWT.FILL;
		data.grabExcessVerticalSpace = false;
		data.grabExcessHorizontalSpace = false;

		bottomPanel.setLayoutData(data);
		bottomPanel.setLayout(new GridLayout(6, false));

		(new Label(bottomPanel, SWT.NONE)).setText("Timeline calculator");
		timelineScale = new Scale(bottomPanel, SWT.NONE);
		timelineScale.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		(new Label(bottomPanel, SWT.NONE)).setText("Timeline value");
		valueLabel = new Label(bottomPanel, SWT.NONE);

		(new Label(bottomPanel, SWT.NONE)).setText("Date");

		dateLabel = new Label(bottomPanel, SWT.NONE);
		dateLabel.setText("-");

		refreshTimelineScale(0, 50, null);

	}

	private static void refreshTimelineScale(int min, int max, final Map<Integer, Date> timeline) {
		timelineScale.setMinimum(min);
		timelineScale.setMaximum(max);
		timelineScale.setIncrement(1);
		timelineScale.setPageIncrement(5);

		if (scaleListener != null) {
			timelineScale.removeSelectionListener(scaleListener);
		}

		scaleListener = new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				setScaleSelection(timelineScale.getSelection(), timeline);
			}
		};

		timelineScale.addSelectionListener(scaleListener);
		timelineScale.setEnabled(timeline != null);

		timelineScale.setSelection(0);
		setScaleSelection(0, timeline);
	}

	private static void setScaleSelection(int selection, Map<Integer, Date> timeline) {
		valueLabel.setText(String.valueOf(selection));
		if (timeline != null) {
			Date date = timeline.get(Integer.valueOf(selection));
			if (date != null) {
				dateLabel.setText(DateUtils.formatDate(date));
			}
		}
		bottomPanel.layout(true, true);
	}

	private void paint() {
		EnumSet<Indicator> selectedIndicators = indicatorsPanel.getSelectedIndicators();
		if (!selectedIndicators.isEmpty()) {
			paint(UIUtils.getDateFromDateTimeWidget(dateFrom), UIUtils.getDateFromDateTimeWidget(dateTo), getSelectedCompanyId(),
					selectedIndicators);
		}
	}

	public void paint(Date from, Date to, Company company) {
		EnumSet<Indicator> indicators = EnumSet.noneOf(Indicator.class);
		indicators.add(Indicator.CLOSING_PRICE);

		UIUtils.setDateInDateTimeWidget(dateFrom, from);
		UIUtils.setDateInDateTimeWidget(dateTo, to);
		companyCombo.setText(company.getName());

		paint(from, to, company.getId(), indicators);
	}

	private void paint(Date from, Date to, Long companyId, EnumSet<Indicator> indicators) {
		ExchangeDao exDao = DaoProvider.getExchangeDao();
		List<Exchange> exList = exDao.getByCompanyBetweenDates(companyId, from, to);
		ChartBean chartBean = getChartBean(indicators, exList, companyId);

		LineChart lineChart = new LineChart();
		chart = lineChart.createChart(chartBean);
		refreshChartImage();
		chartCanvas.redraw();

		refreshTimelineScale(0, lineChart.getMaxValue(), lineChart.getTimelineMap());
	}

	public void adjustPredictionPanel(boolean predict, Date predictFrom, int sessions) {
		predictionPanel.setPredict(predict);
		predictionPanel.setDate(predictFrom);
		predictionPanel.setSessions(sessions);
	}

	private ChartBean getChartBean(EnumSet<Indicator> indicators, List<Exchange> exList, Long companyId) {
		if (!predictionPanel.getPredict()) {
			return new ChartBean(indicators, exList);
		}

		int sessions = predictionPanel.getSessions();
		if (sessions < 1) {
			return new ChartBean(indicators, exList);
		}

		Date predictFrom = predictionPanel.getDate();
		IDataTransformer transformer = new DailyChangeDataTransformer();

		return new ChartBean(indicators, exList, new PredictionDataComposite(companyId, predictFrom, sessions, transformer,
				new NetworkStructure()));
	}

	private Long getSelectedCompanyId() {
		int indx = companyCombo.getSelectionIndex();
		String selectedCompanyName = companyCombo.getItem(indx);
		return (Long) companyCombo.getData(selectedCompanyName);
	}

	private void refreshChartImage() {
		if (chart != null) {
			chartImage = new Image(Display.getDefault(), UIUtils.convertToSWT(chart.createBufferedImage(chartCanvas.getBounds().width,
					chartCanvas.getBounds().height)));
		}
	}

	private void populateCompanyCombo(Combo combo) {
		CompanyDao compDao = DaoProvider.getCompanyDao();
		List<Company> allCompanies = compDao.getAll();
		for (Company c : allCompanies) {
			String companyName = c.getName();
			combo.add(companyName);
			combo.setData(companyName, c.getId());
		}
		combo.setText(combo.getItem(0));
	}

}
